package com.nx.biz.shopify.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.google.gson.Gson;
import com.nx.biz.shopify.api.RedisKeys;
import com.nx.biz.shopify.api.bo.*;
import com.nx.biz.shopify.convert.ShopifyFulfillmentConverter;
import com.nx.biz.shopify.convert.ShopifyFulfillmentEventConverter;
import com.nx.biz.shopify.model.predicate.ShopifyFulfillmentEventPredicate;
import com.nx.biz.shopify.model.predicate.ShopifyFulfillmentPredicate;
import com.nx.biz.shopify.repo.ShopifyFulfillmentEventRepository;
import com.nx.biz.shopify.repo.ShopifyFulfillmentRepository;
import com.nx.biz.shopify.repo.ShopifyOrderRepository;
import com.nx.biz.shopify.repo.SyncSettingsRepository;
import com.nx.biz.shopify.sdk.ShopifySdk.FulfillmentEventSdk;
import com.nx.biz.shopify.sdk.ShopifySdk.FulfillmentOrderSdk;
import com.nx.biz.shopify.sdk.ShopifySdk.FulfillmentSdk;
import com.nx.biz.shopify.sdk.ShopifySdk.model.bo.FulfillmentBo;
import com.nx.biz.shopify.sdk.ShopifySdk.model.bo.FulfillmentEventBo;
import com.nx.biz.shopify.sdk.ShopifySdk.model.bo.FulfillmentOrderBo;
import com.nx.biz.shopify.sdk.ShopifySdk.model.bo.LocationDetailBo;
import com.nx.biz.shopify.sdk.ShopifySdk.model.cmd.*;
import com.nx.biz.shopify.sdk.ShopifySdk.model.constant.FulfillmentOrderStatus;
import com.nx.biz.shopify.sdk.ShopifySdk.model.constant.FulfillmentStatus;
import com.nx.biz.shopify.sdk.hualeiSdk.HualeiClient;
import com.nx.biz.shopify.sdk.hualeiSdk.model.HualeiTrackNumberResp;
import com.nx.biz.shopify.sdk.hualeiSdk.model.HualeiTrackResp;
import com.nx.biz.shopify.support.ShopifyContext;
import com.nx.common.exception.BusinessException;
import com.nx.common.tools.CheckTools;
import com.nx.common.tools.DateTools;
import com.nx.common.tools.RedisUtil;
import com.nx.common.tools.StringTools;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TrackSyncService {

    private static final Gson GSON = new Gson();

    @Resource
    private FulfillmentOrderSdk fulfillmentOrderSdk;
    @Resource
    private FulfillmentSdk fulfillmentSdk;
    @Resource
    private FulfillmentEventSdk fulfillmentEventSdk;
    @Resource
    private HualeiClient hualeiClient;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private ShopifyFulfillmentConverter shopifyFulfillmentConverter;
    @Resource
    private ShopifyFulfillmentEventConverter shopifyFulfillmentEventConverter;

    @Resource
    private ShopifyOrderRepository shopifyOrderRepository;
    @Resource
    private ShopifyFulfillmentRepository shopifyFulfillmentRepository;
    @Resource
    private ShopifyFulfillmentEventRepository shopifyFulfillmentEventRepository;

    @Resource
    private SyncSettingsRepository syncSettingsRepository;
    @Resource
    private ShopifyContext shopifyContext;

    private FulfillmentCreateReq initFulfillmentReq(Long fulfilmentOrderId, LabelPrintedReq labelPrintedReq) {
        FulfillmentCreateReq fulfillmentCreateReq = new FulfillmentCreateReq();
        FulfillmentCreateReq.TrackInfo trackInfo = new FulfillmentCreateReq.TrackInfo();
        trackInfo.setCompany(labelPrintedReq.getCompany());
        trackInfo.setNumber(labelPrintedReq.getTrackNumber());
        trackInfo.setUrl(labelPrintedReq.getTrackUrl());
        trackInfo.setNotify_customer(false);
        fulfillmentCreateReq.getFulfillment().setTracking_info(trackInfo);

        FulfillmentCreateReq.LineItemsByFulfillmentOrder lineItemsByFulfillmentOrder = new FulfillmentCreateReq.LineItemsByFulfillmentOrder();
        lineItemsByFulfillmentOrder.setFulfillment_order_id(fulfilmentOrderId);
        List<FulfillmentCreateReq.LineItemsByFulfillmentOrder> lineItemsByFulfillmentOrderList = new ArrayList<>();
        lineItemsByFulfillmentOrderList.add(lineItemsByFulfillmentOrder);
        fulfillmentCreateReq.getFulfillment().setLine_items_by_fulfillment_order(lineItemsByFulfillmentOrderList);

        return fulfillmentCreateReq;
    }

    private FulfillmentCreateReq initFulfillmentReq(Long fulfilmentOrderId, LabelUpdateReq labelUpdateReq) {
        FulfillmentCreateReq fulfillmentCreateReq = new FulfillmentCreateReq();
        FulfillmentCreateReq.TrackInfo trackInfo = new FulfillmentCreateReq.TrackInfo();
        trackInfo.setCompany(labelUpdateReq.getNewTrackCompany());
        trackInfo.setNumber(labelUpdateReq.getNewTrackNumber());
        trackInfo.setUrl(labelUpdateReq.getTrackUrl());
        trackInfo.setNotify_customer(false);
        fulfillmentCreateReq.getFulfillment().setTracking_info(trackInfo);

        FulfillmentCreateReq.LineItemsByFulfillmentOrder lineItemsByFulfillmentOrder = new FulfillmentCreateReq.LineItemsByFulfillmentOrder();
        lineItemsByFulfillmentOrder.setFulfillment_order_id(fulfilmentOrderId);
        List<FulfillmentCreateReq.LineItemsByFulfillmentOrder> lineItemsByFulfillmentOrderList = new ArrayList<>();
        lineItemsByFulfillmentOrderList.add(lineItemsByFulfillmentOrder);
        fulfillmentCreateReq.getFulfillment().setLine_items_by_fulfillment_order(lineItemsByFulfillmentOrderList);

        return fulfillmentCreateReq;
    }

    private FulfillmentBo labelPrintedByErpAtom(LabelPrintedReq labelPrintedReq, ShopifySession shopifySession) throws IOException {
        /** 1. 同步到shopify */
        // 1.1. 向shopify查询fulfillment-order信息
        List<FulfillmentOrderBo> fulfillmentOrderBoList = fulfillmentOrderSdk.listAssignFulfillmentOrderByOrderId(
                Long.valueOf(labelPrintedReq.getOrderId()),
                shopifySession.getShop(),
                shopifyContext.getApiVersion(),
                shopifySession.getAccessToken()
        );
        List<FulfillmentOrderBo> avalilableFulfillmentOrderBoList = fulfillmentOrderBoList.stream()
                .filter(item -> item.getStatus().equals(FulfillmentOrderStatus.open)).collect(Collectors.toList());
        if (CheckTools.isNullOrEmpty(avalilableFulfillmentOrderBoList)) {
            log.warn("未找到fulfillment order，orderId:[{}]", labelPrintedReq.getOrderId());
            return null;
        }
        FulfillmentOrderBo fulfillmentOrderBo = avalilableFulfillmentOrderBoList.get(0);
        log.info("fulfillmentOrderBo: {}", fulfillmentOrderBo);

        // 1.2. 同步到shopify，创建fulfillment
        FulfillmentCreateReq fulfillmentCreateReq = initFulfillmentReq(fulfillmentOrderBo.getId(), labelPrintedReq);
        FulfillmentBo fulfillmentBo = fulfillmentSdk.createFulfillment(
                fulfillmentCreateReq,
                shopifySession.getShop(),
                shopifyContext.getApiVersion(),
                shopifySession.getAccessToken()
        );
        if (fulfillmentBo == null) {
            log.error("未成功创建fulfillment到shopify, fulfillmentCreateReq: {}", fulfillmentCreateReq);
            return null;
        }

        /** 2. 记录同步结果 */
        LocationDetailBo destination = new LocationDetailBo();
        destination.setAddress1(fulfillmentOrderBo.getDestination().getAddress1());
        destination.setAddress2(fulfillmentOrderBo.getDestination().getAddress2());
        destination.setCity(fulfillmentOrderBo.getDestination().getCity());
        destination.setProvince(fulfillmentOrderBo.getDestination().getProvince());
        destination.setCountry(fulfillmentOrderBo.getDestination().getCountry());
        destination.setFirstName(fulfillmentOrderBo.getDestination().getFirst_name());
        destination.setLastName(fulfillmentOrderBo.getDestination().getLast_name());
        destination.setPhone(fulfillmentOrderBo.getDestination().getPhone());
        destination.setZip(fulfillmentOrderBo.getDestination().getZip());
        destination.setEmail(fulfillmentOrderBo.getDestination().getEmail());
        fulfillmentBo.setDestination(destination);

        // 存储fulfillment到redis
        redisUtil.hset(RedisKeys.shopifyFulfillment, String.valueOf(fulfillmentBo.getId()), GSON.toJson(fulfillmentBo));
        Object redisPayload = redisUtil.hget(RedisKeys.shopifyFulfillment, String.valueOf(fulfillmentBo.getId()));
        log.info("redis write fulfillment value: {}", redisPayload);

        // 存储fulfillment到db
        ShopifyFulfillment shopifyFulfillmentBo = shopifyFulfillmentConverter.fulfillmentBo2FulfillmentEo(null, fulfillmentBo);
        shopifyFulfillmentBo.setShopId(String.valueOf(fulfillmentOrderBo.getShopId()));
        shopifyFulfillmentBo.setFulfillmentOrderId(String.valueOf(fulfillmentOrderBo.getId()));
        shopifyFulfillmentBo.setTransferNo(labelPrintedReq.getTransferNo());
        shopifyFulfillmentBo.setSyncFlag(1);
        shopifyFulfillmentRepository.save(shopifyFulfillmentBo);

        // 存储快递单号到shopify order
        ShopifyOrder shopifyOrderBoDb = shopifyOrderRepository.queryById(String.valueOf(fulfillmentBo.getOrder_id()));
        if (CheckTools.isNotNullOrEmpty(shopifyOrderBoDb)) {
            // 修改tracking number 并保存
            List<String> trackNumberList = new ArrayList<>();
            if (CheckTools.isNotNullOrEmpty(shopifyOrderBoDb.getTrackNumber())) {
                trackNumberList = StrUtil.splitTrim(shopifyOrderBoDb.getTrackNumber(), ",");
            }
            trackNumberList.add(labelPrintedReq.getTrackNumber());
            shopifyOrderBoDb.setTrackNumber(String.join(",", trackNumberList));
            shopifyOrderRepository.update(shopifyOrderBoDb);
        }

        return fulfillmentBo;
    }

    public FulfillmentBo labelPrintedByErp(LabelPrintedReq labelPrintedReq, ShopifyOrder shopifyOrder) throws IOException {
        labelPrintedReq.check();
        if (CheckTools.isNullOrEmpty(shopifyOrder.getShopDomain())) {
            throw new BusinessException("订单没有shopDomain");
        }
        ShopifySession matchSession = shopifyContext.getMatchSession(shopifyOrder.getShopDomain());
        if (CheckTools.isNullOrEmpty(matchSession)) {
            throw new BusinessException(String.format("订单：%s的shopDomain：%s没有匹配的会话", shopifyOrder.getId(), shopifyOrder.getShopDomain()));
        }

        /** 1. 查询db， 基于order id + trackingNumber 查询可用的fulfillment */
        ShopifyFulfillmentPredicate predicate = new ShopifyFulfillmentPredicate();
        predicate.setEqOrderId(labelPrintedReq.getOrderId());
        predicate.setNeStatus(FulfillmentStatus.cancelled);
        List<ShopifyFulfillment> oldFulfillmentEoDbList = shopifyFulfillmentRepository.queryList(predicate);


        /** 2. 业务逻辑处理 */
        // 2.1. 没有可用的fulfillment, 直接创建
        if (CheckTools.isNullOrEmpty(oldFulfillmentEoDbList)) {
            return labelPrintedByErpAtom(labelPrintedReq, matchSession);
        }
        // 2.2. 有可用的fulfillment
        else {
            ShopifyFulfillment shopifyFulfillment = oldFulfillmentEoDbList.get(0);

            // 该track number已创建过，直接返回
            List<ShopifyFulfillment> repeatFulfillmentEoDbList = oldFulfillmentEoDbList.stream()
                    .filter(item -> {
                        // 如果运单号、转单号都没有发生变化
                        if (Objects.equals(item.getTrackingNumber(), labelPrintedReq.getTrackNumber())
                                && Objects.equals(item.getTransferNo(), labelPrintedReq.getTransferNo())) {
                            return true;
                        }
                        return false;
                    }).collect(Collectors.toList());
            if (CheckTools.isNotNullOrEmpty(repeatFulfillmentEoDbList)) {
                log.info("本次运单同步，无需更新，订单id：{}，运单号[{}] -> [{}]，转单号[{}] -> [{}]",
                        shopifyFulfillment.getOrderId(), shopifyFulfillment.getTrackingNumber(), labelPrintedReq.getTrackNumber(),
                        shopifyFulfillment.getTransferNo(), labelPrintedReq.getTransferNo());
                return null;
            }
            // 该track number未创建过，更新track number
            else {
                log.info("本次运单同步，需要更新，订单id：{}，运单号[{}] -> [{}]，转单号[{}] -> [{}]",
                        shopifyFulfillment.getOrderId(), shopifyFulfillment.getTrackingNumber(), labelPrintedReq.getTrackNumber(),
                        shopifyFulfillment.getTransferNo(), labelPrintedReq.getTransferNo());
                LabelUpdateReq labelUpdateReq = new LabelUpdateReq();
                labelUpdateReq.setOrderId(labelPrintedReq.getOrderId());
                labelUpdateReq.setOldTrackNumber(oldFulfillmentEoDbList.get(0).getTrackingNumber());
                labelUpdateReq.setNewTrackCompany(labelPrintedReq.getCompany());
                labelUpdateReq.setNewTrackNumber(labelPrintedReq.getTrackNumber());
                labelUpdateReq.setTransferNo(labelPrintedReq.getTransferNo());
                labelUpdateReq.setTrackUrl(labelPrintedReq.getTrackUrl());
                return this.labelUpdatedByErp(labelUpdateReq, matchSession);
            }
        }
    }

    public FulfillmentBo labelUpdatedByErp(LabelUpdateReq labelUpdateReq, ShopifySession shopifySession) throws IOException {
        labelUpdateReq.check();

        /** 1. 查询该order是否已存在fulfillment，如果不存在则无法更新 */
        ShopifyFulfillmentPredicate predicate = new ShopifyFulfillmentPredicate();
        predicate.setEqOrderId(labelUpdateReq.getOrderId());
        List<String> statusList = new ArrayList<>();
        statusList.add(FulfillmentStatus.open);
        statusList.add(FulfillmentStatus.success);
        predicate.setInStatusList(statusList);
        if (CheckTools.isNotNullOrEmpty(labelUpdateReq.getOldTrackNumber())) {
            predicate.setEqTrackNumber(labelUpdateReq.getOldTrackNumber());
        }
        List<ShopifyFulfillment> fulfillmentEoDbList = shopifyFulfillmentRepository.queryList(predicate);
        if (CheckTools.isNullOrEmpty(fulfillmentEoDbList)) {
            log.warn("fulfillment 不存在, 无法更新, orderId[{}], oldTrackNumber[{}]",
                    labelUpdateReq.getOrderId(), labelUpdateReq.getOldTrackNumber());
            return null;
        }
        ShopifyFulfillment fulfillmentEoDb = fulfillmentEoDbList.get(0);

        /** 2. 同步到shopify */
        // 更新tracking到shopify
        FulfillmentCreateReq fulfillmentCreateReq = initFulfillmentReq(Long.valueOf(fulfillmentEoDb.getFulfillmentOrderId()), labelUpdateReq);
        fulfillmentCreateReq.getFulfillment().setId(Long.valueOf(fulfillmentEoDb.getId()));
        FulfillmentBo fulfillmentBo = fulfillmentSdk.updateTracking(
                fulfillmentCreateReq,
                shopifySession.getShop(),
                shopifyContext.getApiVersion(),
                shopifySession.getAccessToken()
        );
        if (fulfillmentBo == null) {
            log.error("未成功创建fulfillment到shopify, fulfillmentCreateReq: {}", fulfillmentCreateReq);
            return null;
        }

        /** 3. 记录同步结果 */
        // 存储fulfillment到redis
        redisUtil.hset(RedisKeys.shopifyFulfillment, String.valueOf(fulfillmentBo.getId()), GSON.toJson(fulfillmentBo));
        Object redisPayload = redisUtil.hget(RedisKeys.shopifyFulfillment, String.valueOf(fulfillmentBo.getId()));
        log.info("redis write fulfillment value: {}", redisPayload);

        // 存储fulfillment到db
        ShopifyFulfillment fulfillmentEo = shopifyFulfillmentConverter.fulfillmentBo2FulfillmentEo(fulfillmentEoDb, fulfillmentBo);
        fulfillmentEo.setTransferNo(labelUpdateReq.getTransferNo());
        fulfillmentEo.setSyncFlag(1);
        shopifyFulfillmentRepository.update(fulfillmentEo);

        // 存储快递单号到shopify order
        ShopifyOrder shopifyOrderEoDb = shopifyOrderRepository.queryById(String.valueOf(fulfillmentBo.getOrder_id()));
        if (CheckTools.isNotNullOrEmpty(shopifyOrderEoDb)) {
            // 修改tracking number 并保存
            List<String> trackNumberList = new ArrayList<>();
            if (CheckTools.isNotNullOrEmpty(shopifyOrderEoDb.getTrackNumber())) {
                trackNumberList = StrUtil.splitTrim(shopifyOrderEoDb.getTrackNumber(), ",");
            }
            if (CheckTools.isNotNullOrEmpty(labelUpdateReq.getOldTrackNumber())) {
                // todo 这里应该是 ne 吧
                trackNumberList = trackNumberList.stream().filter(item -> !item.equals(labelUpdateReq.getOldTrackNumber())).collect(Collectors.toList());
            }
            trackNumberList.add(labelUpdateReq.getNewTrackNumber());
            shopifyOrderEoDb.setTrackNumber(String.join(",", trackNumberList));
            shopifyOrderRepository.update(shopifyOrderEoDb);
        } else {
            log.error("未找到shopify 订单对象: {}", fulfillmentBo.getOrder_id());
        }
        return fulfillmentBo;
    }

    public void syncTrackEventToShopifyAuto(ShopifyFulfillment shopifyFulfillmentEoDb, TrackEvent trackEvent, ShopifySession shopifySession) throws IOException {
        log.info("syncTrackEventToShopifyAuto, trackEvent : {}", trackEvent);
        trackEvent.check();

        /** 1. 数据准备 */
        // 查询db fulfillment event
        ShopifyFulfillmentEventPredicate predicate = new ShopifyFulfillmentEventPredicate();
        predicate.setEqFulfillmentId(shopifyFulfillmentEoDb.getId());
        List<ShopifyFulfillmentEvent> shopifyFulfillmentEventEoList = shopifyFulfillmentEventRepository.queryList(predicate);

        /** 2. 找到需要删除的运踪 & 删除 */
        List<ShopifyFulfillmentEvent> toDelTrackEventInfoList = getToDelList(shopifyFulfillmentEventEoList, trackEvent.getTrackEventInfoList());
        if (CheckTools.isNotNullOrEmpty(toDelTrackEventInfoList)) {
            delTrackEventToShopify(shopifyFulfillmentEoDb, toDelTrackEventInfoList, shopifySession);
        }

        /** 3. 找到需要同步的运踪 & 添加 */
        List<TrackEventInfo> toAddTrackEventInfoList = getToAddList(shopifyFulfillmentEventEoList, trackEvent.getTrackEventInfoList());
        if (CheckTools.isNotNullOrEmpty(toAddTrackEventInfoList)) {
            addTrackEventToShopify(shopifyFulfillmentEoDb, toAddTrackEventInfoList, shopifySession);
        }

        /** 4. 查询并重新更新fulfillment status */
        syncFulfillmentFromShopify(shopifyFulfillmentEoDb, shopifySession);

//        // 从shopify 查询 fulfillment
//        FulfillmentBo fulfillmentBo = fulfillmentSdk.getFulfillmentById(Long.valueOf(shopifyFulfillmentEoDb.getOrderId()), Long.valueOf(shopifyFulfillmentEoDb.getId()));
//
//        // 更新fulfillment
//        if (CheckTools.isNotNullOrEmpty(fulfillmentBo)) {
//            String oldShipmentStatus = shopifyFulfillmentEoDb.getShipmentStatus();
//            String oldStatus = shopifyFulfillmentEoDb.getStatus();
//            shopifyFulfillmentEoDb.setShipmentStatus(fulfillmentBo.getShipment_status());
//            shopifyFulfillmentEoDb.setStatus(fulfillmentBo.getStatus());
//            log.info("fulfillment[{}]->shipment status, [{}]->[{}]", shopifyFulfillmentEoDb.getId(), oldShipmentStatus, shopifyFulfillmentEoDb.getShipmentStatus());
//            log.info("fulfillment[{}]->shipment status, [{}]->[{}]", shopifyFulfillmentEoDb.getId(), oldStatus, shopifyFulfillmentEoDb.getStatus());
//            shopifyFulfillmentService.updateShopifyFulfillment(shopifyFulfillmentEoDb, SecurityTools.getSystemUser());
//        }
    }

    private List<ShopifyFulfillmentEvent> getToDelList(List<ShopifyFulfillmentEvent> shopifyFulfillmentEventEoList, List<TrackEventInfo> trackEventInfoList) {
        List<ShopifyFulfillmentEvent> toDelTrackEventInfoList = new ArrayList<>();

        for (ShopifyFulfillmentEvent shopifyFulfillmentEventEoDb : shopifyFulfillmentEventEoList) {
            Boolean exist = false;
            for (TrackEventInfo trackEventInfo : trackEventInfoList) {
                if (StringTools.equal(shopifyFulfillmentEventEoDb.getMessage(), trackEventInfo.getMessage())
                        && StringTools.equal(shopifyFulfillmentEventEoDb.getHappenedAt(), trackEventInfo.getTime()) // todo，时间格式转换
                        && StringTools.equal(shopifyFulfillmentEventEoDb.getStatus(), trackEventInfo.getStatus())
                ) {
                    exist = true;
                }
            }
            if (!exist) {
                toDelTrackEventInfoList.add(shopifyFulfillmentEventEoDb);
            }
        }
        return toDelTrackEventInfoList;
    }

    private List<TrackEventInfo> getToAddList(List<ShopifyFulfillmentEvent> shopifyFulfillmentEventEoList, List<TrackEventInfo> trackEventInfoList) {
        List<TrackEventInfo> toAddTrackEventInfoList = new ArrayList<>();
        for (TrackEventInfo trackEventInfo : trackEventInfoList) {
            Boolean sync = false;
            for (ShopifyFulfillmentEvent shopifyFulfillmentEventEoDb : shopifyFulfillmentEventEoList) {
                if (StringTools.equal(shopifyFulfillmentEventEoDb.getMessage(), trackEventInfo.getMessage())
                        && StringTools.equal(shopifyFulfillmentEventEoDb.getHappenedAt(), trackEventInfo.getTime()) // todo，时间格式转换
                        && StringTools.equal(shopifyFulfillmentEventEoDb.getStatus(), trackEventInfo.getStatus())
                ) {
                    sync = true;
                }
            }
            if (!sync) {
                // 如果该条记录的发生时间早于同步过的最后一次事件的发生时间，那么跳过 todo

                toAddTrackEventInfoList.add(trackEventInfo);
            }
        }
        return toAddTrackEventInfoList;
    }

    public void delTrackEventToShopify(ShopifyFulfillment shopifyFulfillmentEoDb, List<ShopifyFulfillmentEvent> shopifyFulfillmentEventEoList, ShopifySession shopifySession) throws IOException {
        log.info("delTrackEventToShopify, trackEvent : {}", shopifyFulfillmentEventEoList);

        /** 1. 同步到shopify */
        for (ShopifyFulfillmentEvent shopifyFulfillmentEventEo : shopifyFulfillmentEventEoList) {

            // 同步到shopify
            fulfillmentEventSdk.deleteFulfillmentEvent(
                    Convert.toLong(shopifyFulfillmentEventEo.getOrderId()),
                    Convert.toLong(shopifyFulfillmentEoDb.getId()),
                    Convert.toLong(shopifyFulfillmentEventEo.getId()),
                    shopifySession.getShop(),
                    shopifyContext.getApiVersion(),
                    shopifySession.getAccessToken()
            );

            // 删除db中的事件
            shopifyFulfillmentEventRepository.deleteById(shopifyFulfillmentEventEo.getId());
        }
    }

    public void addTrackEventToShopify(ShopifyFulfillment fulfillmentEo, List<TrackEventInfo> toAddTrackEventInfoList, ShopifySession shopifySession) throws IOException {
        /** 1. 逐条 同步到shopify */
        for (TrackEventInfo trackEventInfo : toAddTrackEventInfoList) {
            // 如果该条记录的发生时间早于同步过的最后一次事件的发生时间，那么跳过 todo


            //将trackEvent Info翻译为shopify的信息
            FulfillmentEventBo fulfillmentEventBoRequest = shopifyFulfillmentEventConverter.trackEventInfo2ShopifyEvent(trackEventInfo);

            // 同步到shopify
            FulfillmentEventBo fulfillmentEventBo = fulfillmentEventSdk.createFulfillmentEvent(
                    Convert.toLong(fulfillmentEo.getOrderId()),
                    Convert.toLong(fulfillmentEo.getId()),
                    fulfillmentEventBoRequest,
                    shopifySession.getShop(),
                    shopifyContext.getApiVersion(),
                    shopifySession.getAccessToken()
            );

            // 将结果保存下来
            ShopifyFulfillmentEvent fulfillmentEventEo = new ShopifyFulfillmentEvent();
            BeanUtils.copyProperties(fulfillmentEventBo, fulfillmentEventEo);
            fulfillmentEventEo.setId(String.valueOf(fulfillmentEventBo.getId()));
            fulfillmentEventEo.setFulfillmentId(String.valueOf(fulfillmentEventBo.getFulfillmentId()));
            fulfillmentEventEo.setOrderId(String.valueOf(fulfillmentEventBo.getOrderId()));
            fulfillmentEventEo.setShopId(String.valueOf(fulfillmentEventBo.getShopId()));
            fulfillmentEventEo.setLatitude(trackEventInfo.getLatitude());
            fulfillmentEventEo.setLongitude(trackEventInfo.getLongitude());
            fulfillmentEventEo.setHappenedAt(trackEventInfo.getTime());
            shopifyFulfillmentEventRepository.save(fulfillmentEventEo);
        }
    }

    public void syncFulfillmentFromShopify(ShopifyFulfillment fulfillmentEoDb, ShopifySession shopifySession) throws IOException {
        // 从shopify 查询 fulfillment
        FulfillmentBo fulfillmentBo = fulfillmentSdk.getFulfillmentById(
                Convert.toLong(fulfillmentEoDb.getOrderId()),
                Convert.toLong(fulfillmentEoDb.getId()),
                shopifySession.getShop(),
                shopifyContext.getApiVersion(),
                shopifySession.getAccessToken()
        );

        // db更新fulfillment
        if (CheckTools.isNotNullOrEmpty(fulfillmentBo)) {
            String oldShipmentStatus = fulfillmentEoDb.getShipmentStatus();
            String oldStatus = fulfillmentEoDb.getStatus();
            fulfillmentEoDb.setShipmentStatus(fulfillmentBo.getShipment_status());
            fulfillmentEoDb.setStatus(fulfillmentBo.getStatus());
            log.info("fulfillment[{}]->shipment status, [{}]->[{}]", fulfillmentEoDb.getId(), oldShipmentStatus, fulfillmentEoDb.getShipmentStatus());
            log.info("fulfillment[{}]->shipment status, [{}]->[{}]", fulfillmentEoDb.getId(), oldStatus, fulfillmentEoDb.getStatus());
            shopifyFulfillmentRepository.update(fulfillmentEoDb);
        }

        // redis更新fulfillment
        redisUtil.hset(RedisKeys.shopifyFulfillment, fulfillmentBo.getId(), GSON.toJson(fulfillmentBo));
    }

    /* ------------------------------------------------------------------------------------------- */

    /**
     * 从erp同步运单到shopify
     */
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public void syncTrackNumber(List<ShopifyOrder> shopifyOrderEoList) {
        List<String> orderIdList = shopifyOrderEoList.stream().map(ShopifyOrder::getId).collect(Collectors.toList());

        // 查询华磊erp，获取track number
        List<HualeiTrackNumberResp> trackNumberRespList = hualeiClient.getTrackingNumberBatch(orderIdList);

        // 查询setting信息，获取运输公司、url
        List<SyncSettings> syncSettingConfigList = syncSettingsRepository.queryList(null);
        SyncSettings syncSettings = null;
        if (CheckTools.isNullOrEmpty(syncSettingConfigList)) {
            syncSettings = new SyncSettings();
            syncSettings.setTrackCompany("默认运输公司");
            syncSettings.setTrackCompanyUrl("https://www.baidu.com");
        } else {
            syncSettings = syncSettingConfigList.get(0);
        }

        // 逐一处理
        for (HualeiTrackNumberResp trackNumberResp : trackNumberRespList) {
            // 如果没有运单号，跳过
            if (CheckTools.isNullOrEmpty(trackNumberResp.getOrderReferencecode())) {
                continue;
            }
            // 找到对应的shopify order，同步给shopify
            for (ShopifyOrder shopifyOrderEoDb : shopifyOrderEoList) {
                if (shopifyOrderEoDb.getId().equals(trackNumberResp.getOrderCustomerinvoicecode())) {
                    // 同步到shopify
                    LabelPrintedReq labelPrintedReq = new LabelPrintedReq();
                    labelPrintedReq.setOrderId(shopifyOrderEoDb.getId());
                    labelPrintedReq.setTrackNumber(trackNumberResp.getOrderReferencecode());
                    labelPrintedReq.setTransferNo(trackNumberResp.getOrderServeinvoicecode());
                    //todo, 生成一个运踪的url
                    labelPrintedReq.setTrackUrl(syncSettings.getTrackCompanyUrl());
                    labelPrintedReq.setCompany(syncSettings.getTrackCompany());
                    this.labelPrintedByErp(labelPrintedReq, shopifyOrderEoDb);
                }
            }
        }
    }

    // 同步运踪
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public void syncTrackEvent(List<ShopifyFulfillment> shopifyFulfillmentEoList) {
        List<String> orderIdList = shopifyFulfillmentEoList.stream().map(ShopifyFulfillment::getOrderId).collect(Collectors.toList());

        /** 1.向华磊erp查询这些订单运踪 (按快递单号查询) */
        List<HualeiTrackResp> trackRespList = hualeiClient.selectTrackBatch(orderIdList);
        if (CheckTools.isNullOrEmpty(trackRespList)) {
            log.info("从华磊erp查询运踪，未找到，直接返回");
            return;
        }
        HualeiTrackResp trackResp = trackRespList.get(0);

        // 如果没有运踪，跳过
        if (CheckTools.isNullOrEmpty(trackResp.getData())) {
            log.info("从华磊erp查询运踪，运踪的data为空，直接返回");
            return;
        }

        /** 2.查询结果 按时间从前到后排序 */
        List<HualeiTrackResp.TrackItem> dataList = trackResp.getData();
//        dataList = dataList.stream().filter(item->item.getReferenceNumber().equals("5765571912313213-C01")).collect(Collectors.toList());//测试
        for (HualeiTrackResp.TrackItem trackItem : dataList) {
            trackItem.getTrackDetails().sort(new Comparator<HualeiTrackResp.TrackItem.TrackDetailsDTO>() {
                @Override
                public int compare(HualeiTrackResp.TrackItem.TrackDetailsDTO item1, HualeiTrackResp.TrackItem.TrackDetailsDTO item2) {
                    Date date1 = DateTools.parseDate(item1.getTrackDate(), DateTools.HHMMSS);
                    Date date2 = DateTools.parseDate(item2.getTrackDate(), DateTools.HHMMSS);
                    int diff = DateTools.compare(date1, date2);
                    if (diff > 0) {
                        return 1;
                    } else if (diff < 0) {
                        return -1;
                    }
                    return 0; //相等为0
                }
            });
        }

        /** 3.查询结果，逐一同步给shopify */
        for (ShopifyFulfillment fulfillmentEo : shopifyFulfillmentEoList) {
            String shopDomain = fulfillmentEo.getShopDomain();
            if (CheckTools.isNullOrEmpty(shopDomain)) {
                log.error("fulfillment：{} 的shopDomain为空，跳过处理", fulfillmentEo.getId());
                continue;
            }
            ShopifySession matchSession = shopifyContext.getMatchSession(shopDomain);
            if (CheckTools.isNullOrEmpty(matchSession)) {
                log.error("fulfillment：{} 的shopDomain：{} 匹配的会话为空，跳过处理", fulfillmentEo.getId(), shopDomain);
                continue;
            }

            for (HualeiTrackResp.TrackItem trackItem : dataList) {
                // todo 这里运踪只关联转单号、订单号，没有内部单号，但是shopify是把华磊的内部单号当做运单号
                // 所以这里还是只能使用订单号关联，但是粒度会不会太粗了
                if (trackItem.getReferenceNumber().equals(fulfillmentEo.getOrderId())) {
                    // 翻译映射
                    TrackEvent trackEvent = shopifyFulfillmentEventConverter.toTrackEvent(fulfillmentEo.getOrderId(), trackItem);

                    // 同步到shopify
                    this.syncTrackEventToShopifyAuto(fulfillmentEo, trackEvent, matchSession);
                }
            }
        }
    }

}
